#include "linux/of.h"
#include "linux/init.h"
#include "linux/slab.h"
#include "linux/string.h"
#include "linux/printk.h"
#include "linux/types.h"
#include "linux/byteorder/generic.h"
#include "linux/of_fdt.h"
#include "linux/libfdt.h"
#include "linux/memblock.h"

void *initial_boot_params;

int dt_root_addr_cells;
int dt_root_size_cells;

void *early_init_dt_alloc_memory_arch(uint64_t size, uint64_t align)
{
    void *str = memblock_alloc(size, align);
    if (str)
        memset(str, 0, size);
    return str;
}

static void *unflatten_dt_alloc(void **mem, unsigned long size, unsigned long align)
{
    void *res;

    *mem = PTR_ALIGN(*mem, align);
    res = *mem;
    *mem += size;

    return res;
}

int early_init_dt_verify(void *params)
{

    if (!params)
        return 0;

    if (fdt_check_header(params))
        return 0;

    /* Setup flat device-tree pointer */
    initial_boot_params = params;

    return 1;
}

int of_fdt_is_compatible(void *blob, unsigned long node, char *compat)
{
    char *cp;
    int cplen;
    unsigned long l, score = 0;

    cp = fdt_getprop(blob, node, "compatible", &cplen);
    if (cp == NULL)
        return 0;
    while (cplen > 0)
    {
        score++;
        if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
            return score;
        l = strlen(cp) + 1;
        cp += l;
        cplen -= l;
    }

    return 0;
}

/*
 * 函数用于检查设备树中的某个节点是否与给定的兼容性字符串列表（compat）中的任意一个 兼容。如果匹配，返回最精确匹配的得分（得分越小越优先）；否则返回0。
 * blob: 设备树二进制数据块（DTB），包含设备树的结构和属性
 * node: 设备树节点在blob中的偏移量（地址）
 * compat: 以NULL结尾的兼容性字符串数组（如{"vendor,chip-v2", "generic-device", NULL}）
 */
int of_fdt_match(void *blob, unsigned long node, char **compat)
{
    unsigned int tmp, score = 0;

    if (!compat)
        return 0;

    while (*compat) // 遍历 compat 数组中的每个兼容性字符串
    {
        tmp = of_fdt_is_compatible(blob, node, *compat); // 检查当前 compat 是否匹配
        if (tmp && (score == 0 || tmp < score))          // 如果匹配且得分更优
            score = tmp;                                 // 更新最佳得分
        compat++;                                        // 检查下一个 compat 字符串
    }

    return score; // 返回最佳得分（0 表示无匹配）
}

unsigned long of_get_flat_dt_root(void)
{
    return 0;
}

void *of_get_flat_dt_prop(unsigned long node, char *name, int *size)
{
    return fdt_getprop(initial_boot_params, node, name, size);
}

char *of_flat_dt_get_machine_name(void)
{
    char *name;
    unsigned long dt_root = of_get_flat_dt_root();

    name = of_get_flat_dt_prop(dt_root, "model", NULL);
    if (!name)
        name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
    return name;
}

int of_flat_dt_match(unsigned long node, char **compat)
{
    return of_fdt_match(initial_boot_params, node, compat);
}

void *of_flat_dt_match_machine(void *default_match, void *(*get_next_compat)(char ***))
{
    void *data = NULL;
    void *best_data = default_match;
    char **compat;
    unsigned long dt_root;
    unsigned int best_score = ~1, score = 0;

    dt_root = of_get_flat_dt_root();
    while ((data = get_next_compat(&compat)))
    {
        score = of_flat_dt_match(dt_root, compat);
        if (score > 0 && score < best_score)
        {
            best_data = data;
            best_score = score;
        }
    }
    if (!best_data)
    {
        char *prop;
        int size;

        printk("\n unrecognized device tree list:\n[ ");

        prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
        if (prop)
        {
            while (size > 0)
            {
                printk("'%s' ", prop);
                size -= strlen(prop) + 1;
                prop += strlen(prop) + 1;
            }
        }
        printk("]\n\n");
        return NULL;
    }

    printk("Machine model: %s\n", of_flat_dt_get_machine_name());

    return best_data;
}

int of_scan_flat_dt(int (*it)(unsigned long node, char *uname, int depth, void *data), void *data)
{
    void *blob = initial_boot_params;
    char *pathp;
    int offset, rc = 0, depth = -1;

    if (!blob)
        return 0;

    for (offset = fdt_next_node(blob, -1, &depth);
         offset >= 0 && depth >= 0 && !rc;
         offset = fdt_next_node(blob, offset, &depth))
    {

        pathp = fdt_get_name(blob, offset, NULL);
        if (*pathp == '/')
            pathp = kbasename(pathp);
        rc = it(offset, pathp, depth, data);
    }
    return rc;
}

int early_init_dt_scan_root(unsigned long node, char *uname, int depth, void *data)
{
    uint32_t *prop;

    if (depth != 0)
        return 0;

    dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
    dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;

    prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
    if (prop)
        dt_root_size_cells = be32_to_cpup(prop);
    printk("dt_root_size_cells = %x\n", dt_root_size_cells);

    prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
    if (prop)
        dt_root_addr_cells = be32_to_cpup(prop);
    printk("dt_root_addr_cells = %x\n", dt_root_addr_cells);

    /* break now */
    return 1;
}

uint64_t dt_mem_next_cell(int s, uint32_t **cellp)
{
    uint32_t *p = *cellp;

    *cellp = p + s;
    return of_read_number(p, s);
}

void early_init_dt_add_memory_arch(uint64_t base, uint64_t size)
{
    printk("scan memory node! base:%lx size:%lx\n", (unsigned long)base, (unsigned long)size);
    
    memblock_add(base, size);
}

/**
 * early_init_dt_scan_memory - Look for an parse memory nodes
 */
int early_init_dt_scan_memory(unsigned long node, char *uname, int depth, void *data)
{
    char *type = of_get_flat_dt_prop(node, "device_type", NULL);
    uint32_t *reg, *endp;
    int l;

    /* We are scanning "memory" nodes only */
    if (type == NULL)
    {
        /*
         * The longtrail doesn't have a device_type on the
         * /memory node, so look for the node called /memory@0.
         */
        if (depth != 1 || strcmp(uname, "memory@0") != 0)
            return 0;
    }
    else if (strcmp(type, "memory") != 0)
        return 0;

    reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
    if (reg == NULL)
        reg = of_get_flat_dt_prop(node, "reg", &l);
    if (reg == NULL)
        return 0;

    endp = reg + (l / sizeof(uint32_t));

    printk("memory scan node %s, reg size %d,\n", uname, l);

    while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells))
    {
        uint64_t base, size;

        base = dt_mem_next_cell(dt_root_addr_cells, &reg);
        size = dt_mem_next_cell(dt_root_size_cells, &reg);

        if (size == 0)
            continue;

        early_init_dt_add_memory_arch(base, size);
    }

    return 0;
}

void early_init_dt_scan_nodes(void)
{
    ///* Retrieve various information from the /chosen node */
    // chosen 节点是一个特殊的节点，用于在系统启动时传递关键参数和配置信息给内核。
    // 它不描述硬件本身，而是作为引导加载程序（如 U-Boot）与内核之间的通信桥梁 ，存储启动过程中需要动态传递的数据。
    // 传递启动参数 ：如内核命令行（bootargs）。
    // 指定内存配置 ：如初始化内存盘（initrd）的地址范围。
    // 保留内存区域 ：标记引导加载程序或固件占用的内存区域。
    // 其他动态信息 ：如系统固件版本、硬件状态等。
    // of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);

    /* Initialize {size,address}-cells info */
    of_scan_flat_dt(early_init_dt_scan_root, NULL);

    ///* Setup memory, calling early_init_dt_add_memory_arch */
    of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}

void of_node_init(struct device_node *node)
{
	//kobject_init(&node->kobj, &of_node_ktype);
	//node->fwnode.type = FWNODE_OF;
}

char *of_node_full_name( struct device_node *np)
{
	return np ? np->full_name : "<no-node>";
}

/**
 * 解析并填充设备树节点的属性列表
 * blob:      FDT数据指针
 * offset:    当前节点在FDT中的偏移量
 * mem:       内存分配指针（用于动态增长的内存池）
 * np:        目标设备节点结构体
 * nodename:  节点原始名称（可能含路径或单元地址）
 * dryrun:    是否仅计算内存不实际操作
 *
 * 功能描述：
 *   1. 遍历节点所有属性，构建属性链表
 *   2. 特殊处理 phandle 相关属性
 *   3. 自动生成缺失的 name 属性
 */
static void populate_properties(void *blob,
                                int offset,
                                void **mem,
                                struct device_node *np,
                                char *nodename,
                                int dryrun)
{
    struct property *pp, **pprev = NULL; // pp:当前属性，pprev:链表指针的指针
    int cur;                             // 当前属性偏移量
    int has_name = 0;                    // 标记是否存在显式name属性

    //  属性链表初始化,初始指向节点属性链表的头部指针
    pprev = &np->properties;

    // 遍历节点所有属性
    for (cur = fdt_first_property_offset(blob, offset);
         cur >= 0;
         cur = fdt_next_property_offset(blob, cur))
    {
        uint32_t *val;
        char *pname;
        int sz;

        val = fdt_getprop_by_offset(blob, cur, &pname, &sz);
        if (!val)
        {;
            printk("Cannot locate property at 0x%x\n", cur);
            continue;
        }

        if (!pname)
        {
            printk("Cannot find property name at 0x%x\n", cur);
            continue;
        }

        if (!strcmp(pname, "name"))
            has_name = true;

        pp = unflatten_dt_alloc(mem, sizeof(struct property), __alignof__(struct property));
        if (dryrun)
            continue;

        /* We accept flattened tree phandles either in
         * ePAPR-style "phandle" properties, or the
         * legacy "linux,phandle" properties.  If both
         * appear and have different values, things
         * will get weird. Don't do that.
         */
        if (!strcmp(pname, "phandle") || !strcmp(pname, "linux,phandle"))
        {
            if (!np->phandle)
                np->phandle = be32_to_cpup(val);
        }

        // 构建属性链表
        pp->name = (char *)pname; // 注意：直接引用FDT中的字符串
        pp->length = sz;
        pp->value = (char *)val; // 保持大端格式
        *pprev = pp;                 // 将当前属性链接到链表
        pprev = &pp->next;           // 移动链表指针到下一个位置
    }

    /* With version 0x10 we may not have the name property,
     * recreate it here from the unit name if absent
     */
    // 处理缺失的name属性（兼容v0x10格式）
    if (!has_name)
    {
        char *p = nodename, *ps = p, *pa = NULL;
        int len;

        /* 解析节点名称中的单元地址：
         * 示例：soc@30000000 -> name="soc"
         *       /fpga@0/eth@1,1e -> name="eth"
         */
        while (*p)
        {
            if ((*p) == '@')
                pa = p; // 记录'@'位置（单元地址起始）
            else if ((*p) == '/')
                ps = p + 1; // 记录最后一个'/'后的位置（子节点名）
            p++;
        }

        /* 计算名称长度：
         * - 若无@则取整个nodename（pa=NULL）
         * - 若@在/前（如/foo@0/bar），取bar直到@前
         */
        if (pa < ps)
            pa = p; // 无效@位置，取到字符串末尾

        len = (pa - ps) + 1; // 包含终止符

        // 分配属性内存（结构体+字符串空间
        pp = unflatten_dt_alloc(mem, sizeof(struct property) + len, __alignof__(struct property));
        if (!dryrun)
        {
            pp->name = "name";
            pp->length = len;
            pp->value = (char *)(pp + 1); // 值存储在属性结构体之后
            *pprev = pp;        // 将生成的name属性加入链表
            pprev = &pp->next;

            // 复制名称并添加终止符
            memcpy(pp->value, ps, len - 1);
            ((char *)pp->value)[len - 1] = 0;
            printk("fixed up name for %s -> %s\n", nodename, (char *)pp->value);
        }
    }

    // 终止属性链表
    if (!dryrun)
        *pprev = NULL; // 设置链表末尾的next指针为NULL
}

/**
 * 填充设备树节点结构并构建层次关系
 *
 * blob:    FDT数据指针
 * offset:  当前节点在FDT中的偏移量
 * mem:     内存分配指针（用于动态增长的内存池）
 * dad:     父节点指针
 * fpsize:  父节点累计路径长度（用于路径重建）
 * pnp:     输出参数，返回新创建的节点指针
 * dryrun:  是否为干跑模式（仅计算内存不实际分配）
 *
 * 返回值:   更新后的累计路径长度（用于子节点路径计算）
 */
static unsigned int populate_node(void *blob,
                                  int offset,
                                  void **mem,
                                  struct device_node *dad,
                                  unsigned int fpsize,
                                  struct device_node **pnp,
                                  int dryrun)
{
    struct device_node *np; // 新设备节点
    char *pathp;            // 节点名称（可能为短路径）
    int l, allocl; // 路径长度/分配长度
    int new_format = 0;     // 新格式标志（非完整路径）

    // 获取节点名称
    pathp = fdt_get_name(blob, offset, &l);
    if (!pathp) // 无效节点处理
    {
        *pnp = NULL;
        return 0;
    }

    allocl = ++l; // 包含终止符'\0'的长度

    /* version 0x10 has a more compact unit name here instead of the full
     * path. we accumulate the full path size using "fpsize", we'll rebuild
     * it later. We detect this because the first character of the name is
     * not '/'.
     */
    // 新旧格式检测与路径处理
    if ((*pathp) != '/') // 新格式检测（路径不以'/'开头）
    {
        new_format = 1;
        if (fpsize == 0) // 根节点特殊处理
        {
            /* root node: special case. fpsize accounts for path
             * plus terminating zero. root node only has '/', so
             * fpsize should be 2, but we want to avoid the first
             * level nodes to have two '/' so we use fpsize 1 here
             */
            fpsize = 1; // 路径累计长度
            allocl = 2; // '/' + '\0'
            l = 1;      // 实际路径长度（空字符串处理为"/"）
            pathp = ""; // 路径置空（后续特殊处理）
        }
        else
        {
            /* account for '/' and path size minus terminal 0
             * already in 'l'
             */
            /*
             * 累计路径长度 = 父路径长度 + 当前节点长度 + '/'
             * 注意：l已包含终止符，故不需额外+1
             */
            fpsize += l;     // 更新累计路径长度
            allocl = fpsize; // 分配空间为总路径长度
        }
    }

    // 分配节点内存（包含路径存储空间）
    np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
                            __alignof__(struct device_node));


    // 干跑模式跳过实际初始化
    if (!dryrun)
    {
        char *fn;

        of_node_init(np);                                // 初始化节点基础结构
        np->full_name = fn = ((char *)np) + sizeof(*np); // 置节点全名存储位置（紧接结构体后）

        if (new_format) // 新格式路径重建
        {
            /* rebuild full path for new format */
            if (dad && dad->parent)
            {
                strcpy(fn, dad->full_name); // 复制父节点路径
                fn += strlen(fn);           // 移动指针到路径末尾
            }
            *(fn++) = '/'; // 添加路径分隔符
        }
        memcpy(fn, pathp, l); // 复制名称到路径末尾

        // 构建父子关系
        if (dad != NULL) // 链接到父节点
        {
            np->parent = dad;         // 设置父指针
            np->sibling = dad->child; // 当前节点成为父节点的第一个子节点
            dad->child = np;          // 更新父节点的子节点指针
        }
    }

    // 填充节点属性
    populate_properties(blob, offset, mem, np, pathp, dryrun);
    if (!dryrun)
    {
        np->name = of_get_property(np, "name", NULL);        // 获取"name"属性
        np->type = of_get_property(np, "device_type", NULL); // 获取设备类型

        // 设置默认值防止空指针
        if (!np->name)
            np->name = "<NULL>";
        if (!np->type)
            np->type = "<NULL>";
    }

    *pnp = np;     // 返回新节点指针
    return fpsize; // 返回更新后的累计路径长度
}

/**
 * 从设备树（FDT）分配并构建设备节点树（device_node）
 * blob:   指向设备树（FDT）数据的指针
 * mem:    用于分配设备节点和属性的内存块
 * dad:    父设备节点指针（若为子树展开）
 * nodepp: 输出参数，返回构建的设备节点树的根节点
 *
 * 返回值:  成功时返回解扁平化后占用的内存大小，失败返回错误码（负数）
 */
static int unflatten_dt_nodes(void *blob, void *mem, struct device_node *dad, struct device_node **nodepp)
{
    struct device_node *root; // 当前根节点指针
    int offset = 0, depth = 0, initial_depth = 0;
#define FDT_MAX_DEPTH 64                    // 允许的最大节点深度（防溢出）
    unsigned int fpsizes[FDT_MAX_DEPTH];    // 记录每个深度节点的全路径名长度
    struct device_node *nps[FDT_MAX_DEPTH]; // 记录每个深度的节点指针
    void *base = mem;                       // 记录内存块的起始地址
    int dryrun = !base;                     // 标志是否仅计算内存需求（dryrun模式）

    if (nodepp)
        *nodepp = NULL;

    /*
     * We're unflattening device sub-tree if @dad is valid. There are
     * possibly multiple nodes in the first level of depth. We need
     * set @depth to 1 to make fdt_next_node() happy as it bails
     * immediately when negative @depth is found. Otherwise, the device
     * nodes except the first one won't be unflattened successfully.
     */
    /*
     * 若存在父节点 `dad`，表示正在展开子树：
     * - 设备树可能存在多个顶层节点，需设置初始深度为 1
     * - 否则 `fdt_next_node()` 遇到负深度会直接终止，导致后续节点无法展开
     */
    if (dad)
        depth = initial_depth = 1;

    // 初始化根节点和路径信息
    root = dad;
    fpsizes[depth] = dad ? strlen(of_node_full_name(dad)) : 0; // 父节点全名长度
    nps[depth] = dad;                                          // 记录当前深度的父节点

    // 主循环：遍历设备树节点
    for (offset = 0;
         offset >= 0 && depth >= initial_depth;        // 有效偏移且深度合法
         offset = fdt_next_node(blob, offset, &depth)) // 获取下一个节点偏移
    {
        // 深度超过最大值时跳过（防止栈溢出）
        if (depth >= FDT_MAX_DEPTH)
            continue;

        // 填充当前节点并分配内存
        fpsizes[depth + 1] = populate_node(blob, offset, &mem,
                                           nps[depth],      // 父节点
                                           fpsizes[depth],  // 父节点路径长度
                                           &nps[depth + 1], // 新节点指针
                                           dryrun);         // 是否实际分配

        // 若 populate_node 返回 0，表示内存不足或解析失败
        if (!fpsizes[depth + 1])
            return mem - base;

        // 更新输出根节点（仅首次有效节点）
        if (!dryrun && nodepp && !*nodepp)
            *nodepp = nps[depth + 1];

        // 记录实际根节点（若无父节点）
        if (!dryrun && !root)
            root = nps[depth + 1];
    }

    // 处理遍历错误（排除正常的未找到错误）
    if (offset < 0 && offset != -FDT_ERR_NOTFOUND)
    {
        printk("Error %d processing FDT\n", offset);
        //return -EINVAL;
        return -1;
    }

    /*
     * Reverse the child list. Some drivers assumes node order matches .dts
     * node order
     */

    /*
     * 反转子节点链表：
     * - 某些驱动依赖节点顺序与 .dts 文件中的声明顺序一致
     * - FDT 存储顺序与 .dts 相反，需反转恢复原始顺序
     */
    //if (!dryrun)
        //reverse_nodes(root);

    // 返回实际使用的内存大小（或 dryrun 时的预估大小）
    return mem - base;
}


/**
 * 将设备树（FDT）解压为设备节点树结构
 * blob:     指向设备树（FDT）数据的指针
 * dad:      父设备节点（用于子树解压）
 * mynodes:  输出参数，返回解压后的设备节点树的根节点
 * dt_alloc: 内存分配函数，需满足对齐要求
 * detached: 是否标记解压后的树为 "分离" 状态（不关联到全局设备树）
 *
 * 返回值:    成功时返回分配的内存块指针，失败返回 NULL
 */

static void *__unflatten_device_tree(void *blob, struct device_node *dad, struct device_node **mynodes, void *(*dt_alloc)(uint64_t size, uint64_t align), int detached)
{
    int size;
    void *mem;

    printk(" -> unflatten_device_tree()\n");

    if (!blob)
    {
        printk("No device tree pointer\n");
        return NULL;
    }

    printk("Unflattening device tree:\n");
    printk("magic: %08x\n", fdt_magic(blob));
    printk("size: %08x\n", fdt_totalsize(blob));
    printk("version: %08x\n", fdt_version(blob));

    if (fdt_check_header(blob))
    {
        printk("Invalid device tree blob header\n");
        return NULL;
    }

    // 第一次遍历：计算所需内存大小
    size = unflatten_dt_nodes(blob, NULL, dad, NULL);
    if (size < 0)
        return NULL;

    // 内存对齐并分配空间
    size = ALIGN(size, 4); // 按 4 字节对齐
    printk("  size is %d, allocating...\n", size);

    /* Allocate memory for the expanded device tree */
    mem = dt_alloc(size + 4, __alignof__(struct device_node)); // 分配内存 + 4字节保护区
    if (!mem)
        return NULL;

    memset(mem, 0, size);
    // 初始化内存并设置溢出保护标记
    *(uint32_t *)(mem + size) = cpu_to_be32(0xdeadbeef); // 末尾写入魔数防溢出

    printk("  unflattening %p...\n", mem);

    // 第二次遍历：实际解压设备树到内存
    unflatten_dt_nodes(blob, mem, dad, mynodes);
    if (be32_to_cpup(mem + size) != 0xdeadbeef)                                         // 检查溢出保护标记是否被覆盖
        printk("End of tree marker overwritten: %08x\n", be32_to_cpup(mem + size)); // 内存越界警告

    // 标记为分离树（若需要）
    if (detached && mynodes)
    {
        // of_node_set_flag(*mynodes, OF_DETACHED);
        // printk("unflattened tree is detached\n");
    }

    printk(" <- unflatten_device_tree()\n");
    return mem; // 返回分配的内存块
}

void unflatten_device_tree(void)
{
    __unflatten_device_tree(initial_boot_params, NULL, &of_root, early_init_dt_alloc_memory_arch, 0);

    /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
     of_alias_scan(early_init_dt_alloc_memory_arch);
}

int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size)
{
	return memblock_reserve(base, size);
}

void early_init_fdt_reserve_self(void)
{
	if (!initial_boot_params)
		return;

	early_init_dt_reserve_memory_arch(__pa(initial_boot_params), fdt_totalsize(initial_boot_params));
}


/* 递归打印设备树节点及子节点 */
static void __of_dump_device_node( struct device_node *np, int depth)
{
    struct property *prop;
    struct device_node *child;
    char indent[32] = {0};
    int i;

    /* 生成缩进字符串（每层2个空格） */
    for (i = 0; i < depth && i < sizeof(indent) - 1; i += 2)
        strcat(indent, "    ");

    /* 打印节点基本信息 */
    printk("%sNode: %s\n", indent, np->full_name);
    printk("%s\t\tname: %s\n", indent, np->name);
    if (np->type && strlen(np->type) > 0)
        printk("%s\t\ttype: %s\n", indent, np->type);
   
    if (np->phandle)
        printk("%s\t\tphandle: 0x%x\n", indent, np->phandle);

    /* 遍历属性列表 */
    for (prop = np->properties; prop != NULL; prop = prop->next)
    {
        printk("%s\t\tProperty: %s\n", indent, prop->name);
        printk("%s\t\t\t\tLength: %d bytes\n", indent, prop->length);

        /* 特殊属性处理 */
        //if (strcmp(prop->name, "name") &&
            //strcmp(prop->name, "phandle") &&
            //strcmp(prop->name, "linux,phandle") &&
            //strcmp(prop->name, "ibm,phandle"))
        {
            /* 尝试以字符串格式打印（适用于可打印属性） */
            if (prop->length > 0 && prop->value[prop->length - 1] == '\0' && strnlen(prop->value, prop->length) > 0)
            {
                printk("%s\t\t\t\tValue: \"%s\"\n", indent, ( char *)prop->value);
            }
            else
            {
                /* 二进制数据以HEX打印（最多显示前16字节） */
                //int print_len = prop->length > 16 ? 16 : prop->length;
                printk("\t\t\t\t");
                for (i = 0; i < prop->length; i++)
                    printk("%x ", (( unsigned char *)prop->value)[i]);
                printk("\n");
            }
        }
    }

    /* 递归处理子节点 */
    for (child = np->child; child != NULL; child = child->sibling)
        __of_dump_device_node(child, depth + 1);
}

/* 打印完整设备树入口函数 */
void of_dump_device_tree(void)
{
    if (!of_root)
    {
        printk("Device tree not initialized!\n");
        return;
    }

    printk("\n================ Device Tree Dump ================\n");
    __of_dump_device_node(of_root, 0);
    printk("=================================================\n\n");
}
